Start of Rails 8 development, 7.0.8.2 & 7.1.3.3 released, Kamal by default, and lots more! | This Week in Rails
Railties に関する変更です
Kamalというアプリケーションデプロイツールが rails のデフォルトになるそうです アプリケーション(群)の構成とデプロイ先のサーバ(群)のIPアドレスなどの基本的な情報を設定すると
仮想マシン
クラウドのサーバインスタンス
などに、Docker環境の構築からアプリのデプロイ、トラフィックの切り替えまでを自動的に行ってくれます
railsアプリケーションを rails new コマンドで生成する際に、config/deploy.yml が生成されるようになります
生成をスキップしたい場合は --skip-kamal オプションを指定してください
ActiveRecordに関する変更です
create_schema というメソッドがあります
DBに新しいスキーマをつくるメソッドです
今回のプルリクエストにてこのメソッドに2つのオプションが追加されました。
:if_not_exists と :force です。それぞれ、
:if_not_exists は「同名のスキーマが存在していない場合のみ、新しいスキーマをつくる」
:force は「同名のスキーマがあれば削除し、新しいスキーマをつくる」
というオプションです
PostgreSQLで使えるオプションをActiveRecordのPostgreSQLアダプターに実装している形ですね
ActiveRecordに関する変更です
touch_allというメソッドがあります
現在のリレーション内のすべてのレコードの updated_at/updated_on 属性を、現在の時刻または指定された時刻に更新します
code:rb
# Touch all records
Person.all.touch_all
# => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"
code:rb
# Touch multiple records with a specified time
Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
# => "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"
また、引数として属性名を渡す場合、渡した属性と updated_at/updated_on 属性を同時に更新します
code:rb
# Touch multiple records with a custom attribute
Person.all.touch_all(:created_at)
# => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"
このメソッドは、エイリアスされた属性名を引数に渡した場合に不具合が発生する場合がありました
具体的にはupdate用の属性に対してエイリアスが設定されている場合です
code:rb
create_table :users do |t|
t.timestamp :legacy_updated_at
end
class User < ActiveRecord::Base
alias_attribute :updated_at, :legacy_updated_at
end
User.touch_all(:updated_at)
# ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: multiple assignments to same column "legacy_updated_at"
これは touch_all の内部で update_all を呼んでおり、
update_all に渡す値が legacy_updated_at: time, updated_at: time となるためです
update_all は
It does not instantiate the involved models and it does not trigger Active Record callbacks or validations. However, values passed to update_all will still go through Active Record’s normal type casting and serialization.
型キャストやシリアライズはされるので、
code:rb
User.update_all(legacy_updated_at: time, updated_at: time)
は、updated_at attributeはエイリアスされた legacy_updated_at に変換されて
"UPDATE \"users\" SET \"legacy_updated_at\" = '2018-01-04 22:55:23.132670', \"legacy_updated_at\" = '2018-01-04 22:55:23.132670'" のようなクエリがつくられます
同じカラムに対して値の割当を複数回しているので StatementInvalid エラーになる、ということです
今回のプルリクエストでは、touch_all 内部の update_all に渡す引数をつくるメソッドを修正しています
これにより、
修正前まで update_all(legacy_updated_at: time, updated_at: time) となっていた部分が
修正後は update_all(legacy_updated_at: time) となるようになりました
update_all にわたす前に updated_at を lagacy_updated_at に変換したうえで attributes の重複チェックをすることで、修正前の問題を解消しています